package log;

import storage.file.*;
import static storage.file.Block.*;
import java.util.Iterator;


import server.*;



public class LogManager implements Iterable<BasicLogRecord>{

	public static final int LAST_POS = 0;
	private String logfile;
	private Block myblock = new Block();
	private BlockID currentblock;
	private int currentpos;

	public LogManager(String logfile) {
		this.logfile = logfile;
		int logsize = SimpleDB.fileManager().SizeOfLog(logfile);
		if (logsize == 0)
			appendNewBlock();
		else {
			currentblock = new BlockID(logfile, logsize - 1);
			myblock.read(currentblock);
			currentpos = getLastRecordPosition() + INT_SIZE;
		}
	}

	private void flush() {
		myblock.write(currentblock);
	}

	public void flush(int lsn) {
		if (lsn >= currentLSN())
			flush();
	}
	private void appendNewBlock() {
		setLastRecordPosition(0);
		currentpos = INT_SIZE;
		currentblock = myblock.append(logfile);
	}
	private void finalizeRecord() {
		myblock.setInt(currentpos, getLastRecordPosition());
		setLastRecordPosition(currentpos);
		currentpos += INT_SIZE;
	}
	private int getLastRecordPosition() {
		return myblock.getInt(LAST_POS);
	}

	private void setLastRecordPosition(int pos) {
		myblock.setInt(LAST_POS, pos);
	}

	public synchronized Iterator<BasicLogRecord> iterator() {
		flush();
		return new LogIterator(currentblock);
	}
	public synchronized int append(Object[] rec) {
		int recsize = INT_SIZE; 
		for (Object obj : rec)
			recsize += size(obj);
		if (currentpos + recsize >= BLOCK_SIZE) { // the log record doesn't fit,
			flush(); // so move to the next block.
			appendNewBlock();
		}
		for (Object obj : rec)
			appendVal(obj);
		finalizeRecord();
		return currentLSN();
	}

	private void appendVal(Object val) {

		if (val instanceof String)
			myblock.setString(currentpos, (String) val);
		if (val instanceof Integer)
			myblock.setInt(currentpos, (Integer) val);
		if (val instanceof Boolean)
			myblock.setBoolean(currentpos, (Boolean) val);
		if (val instanceof Float)
			myblock.setFloat(currentpos, (Float) val);
		if (val instanceof Double)
			myblock.setDouble(currentpos, (Double) val);
		if (val instanceof Long)
			myblock.setLong(currentpos, (Long) val);
		if (val instanceof Byte)
			myblock.SetByte(currentpos, (Byte) val);

		currentpos += size(val);
	}

	private int size(Object val) {

		if (val instanceof String) {
			String sval = (String) val;
			return STRING_SIZE(sval.length());
		}
		if (val instanceof Integer) {
			return INT_SIZE;
		}

		if (val instanceof Boolean) {
			return BOOLEAN_SIZE;
		}

		if (val instanceof Float) {
			return FLOAT_SIZE;
		}

		if (val instanceof Double) {
			return DOUBLE_SIZE;
		} else
			return INT_SIZE;
	}
	private int currentLSN() {
		return currentblock.offset();
	}
	
}
